package com.szcinda.service.dto.notice;

import com.szcinda.repository.*;
import com.szcinda.service.PageResult;
import com.szcinda.service.SnowFlakeFactory;
import com.szcinda.service.TypeStringUtils;
import com.szcinda.service.dto.log.LogCreateDto;
import com.szcinda.service.dto.log.LogService;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import javax.persistence.criteria.Predicate;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Service
@Transactional
public class NoticeServiceImpl implements NoticeService {

    private final NoticeRepository noticeRepository;
    private final SnowFlakeFactory snowFlakeFactory;
    private final UserRepository userRepository;
    private final OrganizationRepository organizationRepository;
    private final LogService logService;

    public NoticeServiceImpl(NoticeRepository noticeRepository, UserRepository userRepository, OrganizationRepository organizationRepository, LogService logService) {
        this.noticeRepository = noticeRepository;
        this.userRepository = userRepository;
        this.organizationRepository = organizationRepository;
        this.logService = logService;
        this.snowFlakeFactory = SnowFlakeFactory.getInstance();
    }

    @Override
    public void create(NoticeCreateDto createDto) {
        Assert.isTrue(createDto.getStartTime().isBefore(createDto.getEndTime()), "开始时间必须在结束时间之前");
        Notice notice = new Notice();
        BeanUtils.copyProperties(createDto, notice);
        notice.setId(snowFlakeFactory.nextId("NT"));
        if (createDto.getOrganizationIds() != null && createDto.getOrganizationIds().size() > 0) {
            List<Organization> organizations = organizationRepository.findAll(createDto.getOrganizationIds());
            notice.setOrganizationIds(String.join(",", createDto.getOrganizationIds()));
            notice.setOrganizationNames(organizations.stream().map(Organization::getName).collect(Collectors.joining(",")));
        }
        noticeRepository.save(notice);
        User user = userRepository.findById(createDto.getOpId());
        LogCreateDto logCreateDto = new LogCreateDto(user.getId(), user.getNickName(), "新增内部公告", notice.getTitle());
        logService.create(logCreateDto);
    }

    @Override
    public void update(NoticeUpdateDto updateDto) {
        Notice notice = noticeRepository.findOne(updateDto.getId());
        BeanUtils.copyProperties(updateDto, notice, "id", "version");
        if (updateDto.getOrganizationIds() != null && updateDto.getOrganizationIds().size() > 0) {
            List<Organization> organizations = organizationRepository.findAll(updateDto.getOrganizationIds());
            notice.setOrganizationIds(String.join(",", updateDto.getOrganizationIds()));
            notice.setOrganizationNames(organizations.stream().map(Organization::getName).collect(Collectors.joining(",")));
        }
        noticeRepository.save(notice);
        User user = userRepository.findById(updateDto.getOpId());
        LogCreateDto logCreateDto = new LogCreateDto(user.getId(), user.getNickName(), "修改内部公告", notice.getTitle());
        logService.create(logCreateDto);
    }

    @Override
    public void delete(String id, String userId) {
        Notice notice = noticeRepository.findById(id);
        noticeRepository.delete(notice);
        User user = userRepository.findById(userId);
        LogCreateDto logCreateDto = new LogCreateDto(user.getId(), user.getNickName(), "删除内部公告", notice.getTitle());
        logService.create(logCreateDto);
    }

    @Override
    public List<Notice> getAll(String organizationId) {
        Specification<Notice> specification = ((root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            Predicate start = criteriaBuilder.lessThanOrEqualTo(root.get("startTime"), LocalDate.now());
            predicates.add(start);
            Predicate createTimeEnd = criteriaBuilder.greaterThanOrEqualTo(root.get("endTime"), LocalDate.now());
            predicates.add(createTimeEnd);
            Predicate organizationIds = criteriaBuilder.like(root.get("organizationIds"), "%" + organizationId + "%");
            predicates.add(organizationIds);
            Predicate status = criteriaBuilder.equal(root.get("status"), TypeStringUtils.notice_status2);
            predicates.add(status);
            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
        });
        Sort order = new Sort(Sort.Direction.DESC, "createTime");
        return noticeRepository.findAll(specification, order);
    }

    @Override
    public PageResult<Notice> query(NoticeQueryDto params) {
        Specification<Notice> specification = ((root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            if (!StringUtils.isEmpty(params.getTitle())) {
                Predicate site = criteriaBuilder.like(root.get("title").as(String.class), params.getTitle().trim() + "%");
                predicates.add(site);
            }
            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
        });
        Sort sort = new Sort(Sort.Direction.DESC, "createTime");
        Pageable pageable = new PageRequest(params.getPage() - 1, params.getPageSize(), sort);
        Page<Notice> details = noticeRepository.findAll(specification, pageable);
        return PageResult.of(details.getContent(), params.getPage(), params.getPageSize(), details.getTotalElements());
    }

    @Override
    public PageResult<Notice> queryHistory(NoticeQueryDto params) {
        Specification<Notice> specification = ((root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            if (!StringUtils.isEmpty(params.getTitle())) {
                Predicate site = criteriaBuilder.like(root.get("title").as(String.class), params.getTitle().trim() + "%");
                predicates.add(site);
            }
            if (params.getCreateTimeStart() != null) {
                Predicate createTimeStart = criteriaBuilder.greaterThanOrEqualTo(root.get("createTime"), params.getCreateTimeStart().atStartOfDay());
                predicates.add(createTimeStart);
            }
            if (params.getCreateTimeEnd() != null) {
                Predicate createTimeEnd = criteriaBuilder.lessThan(root.get("createTime"), params.getCreateTimeEnd().plusDays(1).atStartOfDay());
                predicates.add(createTimeEnd);
            }
            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
        });
        Sort sort = new Sort(Sort.Direction.DESC, "createTime");
        Pageable pageable = new PageRequest(params.getPage() - 1, params.getPageSize(), sort);
        Page<Notice> details = noticeRepository.findAll(specification, pageable);
        return PageResult.of(details.getContent(), params.getPage(), params.getPageSize(), details.getTotalElements());
    }
}
